package httpserver

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	"workflow-gui/internal/database"
	"workflow-gui/internal/gui"
	"workflow-gui/internal/workflow"
)

// Server represents the HTTP server for GUI service
type Server struct {
	port       int
	guiManager *gui.Manager
	db         *database.DB
	mu         sync.Mutex
	busy       bool
}

// NewServer creates a new HTTP server
func NewServer(port int, dbPath string) (*Server, error) {
	db, err := database.Open(database.Config{Path: dbPath})
	if err != nil {
		return nil, fmt.Errorf("failed to open database: %w", err)
	}

	return &Server{
		port:       port,
		guiManager: gui.NewManager(),
		db:         db,
	}, nil
}

// Start starts the HTTP server
func (s *Server) Start() error {
	http.HandleFunc("/health", s.handleHealth)
	http.HandleFunc("/create", s.handleCreate)
	http.HandleFunc("/workflows", s.handleListWorkflows)
	http.HandleFunc("/workflow/", s.handleGetWorkflow)
	http.HandleFunc("/api/workflows", s.handleAPIWorkflows)
	http.HandleFunc("/gui/main", s.handleShowMainWindow)

	addr := fmt.Sprintf(":%d", s.port)
	log.Printf("Starting GUI HTTP server on %s", addr)
	return http.ListenAndServe(addr, nil)
}

// handleHealth handles health check requests
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}

// handleCreate handles workflow creation requests
func (s *Server) handleCreate(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Check if busy
	s.mu.Lock()
	if s.busy {
		s.mu.Unlock()
		http.Error(w, "Another request is in progress", http.StatusConflict)
		return
	}
	s.busy = true
	s.mu.Unlock()

	defer func() {
		s.mu.Lock()
		s.busy = false
		s.mu.Unlock()
	}()

	// Parse request
	var req map[string]interface{}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	// Extract parameters
	summary, _ := req["summary"].(string)
	motivation, _ := req["motivation"].(string)
	timeoutSec, _ := req["timeout"].(float64)

	timeout := time.Duration(timeoutSec) * time.Second
	if timeout == 0 {
		timeout = 120 * time.Second
	}

	// Show GUI form
	result, err := s.guiManager.ShowCreateWorkflowForm(gui.WorkflowRequest{
		Summary:    summary,
		Motivation: motivation,
		Timeout:    timeout,
	})

	if err != nil {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"error": err.Error(),
		})
		return
	}

	// Check if cancelled
	if result.Cancelled {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"cancelled": true,
		})
		return
	}

	// Check for error
	if result.Error != "" {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"error": result.Error,
		})
		return
	}

	// Create workflow in database
	wf := &workflow.WorkflowData{
		Meta: workflow.Meta{
			ChangeID:      result.ChangeID,
			CreatedAt:     time.Now(),
			UpdatedAt:     time.Now(),
			Status:        "in_progress",
			WorkflowStage: "draft", // 设置初始 workflow_stage
			SessionID:     fmt.Sprintf("session-%d", time.Now().Unix()),
			Round:         1,
			Complexity:    result.Complexity,
		},
		Proposal: workflow.Proposal{
			Summary:    summary,
			Motivation: motivation,
			Scope: workflow.ProposalScope{
				ChangeType:      "feature",
				Priority:        "P1",
				BreakingChanges: false,
			},
		},
		SchemaVersion: "2.0",
	}

	dao := database.NewProjectDAO(s.db)
	if err := dao.Create(wf); err != nil {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"error": fmt.Sprintf("Failed to save workflow: %v", err),
		})
		return
	}

	// Return success
	json.NewEncoder(w).Encode(map[string]interface{}{
		"result": map[string]interface{}{
			"change_id":  result.ChangeID,
			"complexity": result.Complexity,
		},
	})
}

// handleListWorkflows handles listing all workflows
func (s *Server) handleListWorkflows(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	dao := database.NewProjectDAO(s.db)
	projects, err := dao.List()
	if err != nil {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"error": fmt.Sprintf("Failed to list workflows: %v", err),
		})
		return
	}

	// Convert to response format
	workflows := make([]map[string]interface{}, 0, len(projects))
	for _, project := range projects {
		workflows = append(workflows, map[string]interface{}{
			"change_id":      project.ChangeID,
			"name":           project.Name,
			"status":         project.Status,
			"workflow_stage": project.WorkflowStage,
			"created_at":     project.CreatedAt,
			"updated_at":     project.UpdatedAt,
		})
	}

	json.NewEncoder(w).Encode(map[string]interface{}{
		"workflows": workflows,
		"total":     len(workflows),
	})
}

// handleGetWorkflow handles getting a specific workflow
func (s *Server) handleGetWorkflow(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Extract change_id from URL path
	changeID := r.URL.Path[len("/workflow/"):]
	if changeID == "" {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"error": "Missing change_id",
		})
		return
	}

	dao := database.NewProjectDAO(s.db)
	project, err := dao.GetByChangeID(changeID)
	if err != nil {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"error": fmt.Sprintf("Failed to get workflow: %v", err),
		})
		return
	}

	json.NewEncoder(w).Encode(map[string]interface{}{
		"workflow": project,
	})
}

// handleAPIWorkflows handles API requests for WPF window
func (s *Server) handleAPIWorkflows(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Header().Set("Access-Control-Allow-Origin", "*")

	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	dao := database.NewProjectDAO(s.db)
	projects, err := dao.List()
	if err != nil {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"error": fmt.Sprintf("Failed to list workflows: %v", err),
		})
		return
	}

	// Convert to WPF-friendly format
	workflows := make([]map[string]interface{}, 0, len(projects))
	for _, p := range projects {
		// Load full workflow data to get requirements, tasks, etc.
		fullWorkflow, err := dao.GetByChangeID(p.ChangeID)
		if err != nil {
			log.Printf("Warning: Failed to load full workflow %s: %v", p.ChangeID, err)
			continue
		}

		// Build workflow object
		workflow := map[string]interface{}{
			"id":          p.ChangeID,
			"name":        p.ChangeID, // Use change_id as name for now
			"description": fullWorkflow.Proposal.Summary,
			"status":      p.Status,
			"created_at":  p.CreatedAt.Format(time.RFC3339),
			"updated_at":  p.UpdatedAt.Format(time.RFC3339),
		}

		// Add projects (empty for now, can be extended later)
		workflow["projects"] = []map[string]interface{}{}

		// Add requirements
		requirements := make([]map[string]interface{}, 0, len(fullWorkflow.Spec.Requirements))
		for _, req := range fullWorkflow.Spec.Requirements {
			requirements = append(requirements, map[string]interface{}{
				"id":          req.ID,
				"description": req.Description,
			})
		}
		workflow["requirements"] = requirements

		// Add tasks
		tasks := make([]map[string]interface{}, 0, len(fullWorkflow.Tasks.Items))
		for _, task := range fullWorkflow.Tasks.Items {
			tasks = append(tasks, map[string]interface{}{
				"id":     task.ID,
				"name":   task.Name,
				"status": task.Status,
			})
		}
		workflow["tasks"] = tasks

		// Add deliverables (collect from all tasks)
		deliverables := make([]map[string]interface{}, 0)
		for _, task := range fullWorkflow.Tasks.Items {
			for _, del := range task.Deliverables {
				deliverables = append(deliverables, map[string]interface{}{
					"id":   fmt.Sprintf("%s-%s", task.ID, del.Type),
					"name": del.Path,
				})
			}
		}
		workflow["deliverables"] = deliverables

		workflows = append(workflows, workflow)
	}

	json.NewEncoder(w).Encode(map[string]interface{}{
		"workflows": workflows,
	})
}

// handleShowMainWindow shows the WPF main window
func (s *Server) handleShowMainWindow(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Check if busy
	s.mu.Lock()
	if s.busy {
		s.mu.Unlock()
		http.Error(w, "Another window is already active", http.StatusConflict)
		return
	}
	s.busy = true
	s.mu.Unlock()

	defer func() {
		s.mu.Lock()
		s.busy = false
		s.mu.Unlock()
	}()

	// Show main window
	if err := s.guiManager.ShowMainWindow(); err != nil {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"error": err.Error(),
		})
		return
	}

	json.NewEncoder(w).Encode(map[string]interface{}{
		"success": true,
	})
}
